package cn.lili.modules.search.serviceimpl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.PromotionTypeEnum;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.enums.SceneEnums;
import cn.lili.common.utils.GsonUtils;
import cn.lili.common.utils.SpringContextUtil;
import cn.lili.common.vo.PageVO;
import cn.lili.elasticsearch.ElasticsearchIndexAbstractService;
import cn.lili.elasticsearch.config.ElasticsearchProperties;
import cn.lili.modules.goods.client.GoodsSkuClient;
import cn.lili.modules.goods.entity.dos.*;
import cn.lili.modules.goods.entity.dto.GoodsParamsDTO;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.entity.dto.GoodsSkuDTO;
import cn.lili.modules.goods.entity.dto.ProxyGoodsOperationDTO;
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
import cn.lili.modules.goods.entity.enums.GoodsMarketEnum;
import cn.lili.modules.goods.entity.enums.SalesModeEnum;
import cn.lili.modules.goods.integration.GoodsIntegrationService;
import cn.lili.modules.goods.service.*;
import cn.lili.modules.promotion.client.PromotionsClient;
import cn.lili.modules.promotion.entity.dos.BaseStandardPromotions;
import cn.lili.modules.promotion.entity.dos.PromotionGoods;
import cn.lili.modules.promotion.entity.enums.PromotionsScopeTypeEnum;
import cn.lili.modules.promotion.entity.enums.PromotionsStatusEnum;
import cn.lili.modules.promotion.tools.PromotionTools;
import cn.lili.modules.search.client.CustomWordsClient;
import cn.lili.modules.search.entity.dos.CustomWords;
import cn.lili.modules.search.entity.dos.EsGoodsAttribute;
import cn.lili.modules.search.entity.dos.EsGoodsIndex;
import cn.lili.modules.search.entity.dos.EsSupplierGoodsIndex;
import cn.lili.modules.search.entity.dto.EsDeleteDTO;
import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO;
import cn.lili.modules.search.service.EsGoodsIndexService;
import cn.lili.modules.search.service.EsGoodsSearchService;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.routing.GoodsRoutingKey;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 商品索引业务层实现
 *
 * @author paulG
 * @since 2020/10/14
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class EsGoodsIndexServiceImpl extends ElasticsearchIndexAbstractService implements EsGoodsIndexService {

    private static final String KEY_SUCCESS = "success";
    private static final String KEY_FAIL = "fail";
    private static final String KEY_PROCESSED = "processed";

    private final ElasticsearchProperties elasticsearchProperties;

    private final PromotionsClient promotionsClient;

    private final GoodsSkuService goodsSkuService;

    private final BrandService brandService;

    private final CategoryService categoryService;

    private final StoreGoodsLabelService storeGoodsLabelService;

    private final CustomWordsClient customWordsClient;

    private final Cache<Object> cache;

    private final ElasticsearchOperations restTemplate;

    private final GoodsService goodsService;

    private final EsGoodsSearchService esGoodsSearchService;

    private final ApplicationEventPublisher applicationEventPublisher;



    @Override
    public void init() {
        //获取索引任务标识
        Boolean flag = (Boolean) cache.get(CachePrefix.INIT_INDEX_FLAG.getPrefix());
        //为空则默认写入没有任务
        log.info("flag == {}", flag);
        if (flag == null) {
            cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false);
        }
        //有正在初始化的任务，则提示异常
        if (Boolean.TRUE.equals(flag)) {
            throw new ServiceException(ResultCode.INDEX_BUILDING);
        }

        //初始化标识
        cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), null);
        cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), true, 10L, TimeUnit.MINUTES);

//        ThreadUtil.execAsync(() -> this.generatorEsGoodsIndex(SceneEnums.STORE.name()));
//        ThreadUtil.execAsync(() -> this.generatorEsGoodsIndex(SceneEnums.SUPPLIER.name()));

        ThreadUtil.execAsync(() -> this.generatorEsGoodsIndexByGoods(SceneEnums.STORE.name()));
        ThreadUtil.execAsync(() -> this.generatorEsGoodsIndexByGoods(SceneEnums.SUPPLIER.name()));

    }

    @Override
    public Map<String, Long> getProgress() {
        Map<String, Long> map = (Map<String, Long>) cache.get(CachePrefix.INIT_INDEX_PROCESS.getPrefix());
        if (map == null) {
            return Collections.emptyMap();
        }
        Boolean flag = (Boolean) cache.get(CachePrefix.INIT_INDEX_FLAG.getPrefix());
        map.put("flag", Boolean.TRUE.equals(flag) ? 1L : 0L);
        return map;
    }

    @Override
    public void addIndex(EsGoodsIndex goods) {
        try {
                //分词器分词
                this.analyzeAndSaveWords(goods);
                this.saveIndex(goods);
//                EsGoodsSearchDTO searchDTO = new EsGoodsSearchDTO();
//                searchDTO.setGoodsId(goods.getGoodsId());
//                SearchPage<EsGoodsIndex> page = esGoodsSearchService.searchGoods(searchDTO, new PageVO(),EsGoodsIndex.class);
//                SearchHits<EsGoodsIndex> hits = page.getSearchHits();
//                if(hits.getTotalHits()<1){
//                    this.saveIndex(goods);
//                }
            } catch (Exception e) {
                log.error("为商品[" + goods.getGoodsName() + "]生成索引异常", e);
            }

    }

    /**
     * 添加商品索引
     *
     * @param goods 商品索引信息
     */
    @Override
    public void addIndex(List<EsGoodsIndex> goods) {
        for (EsGoodsIndex esGoodsIndex : goods) {
            this.addIndex(esGoodsIndex);
        }
    }

    @Override
    public void updateIndex(EsGoodsIndex goods) {
        this.addIndex(goods);
    }


    /**
     * 商品分词
     *
     * @param goods 商品信息
     */
    private void analyzeAndSaveWords(EsGoodsIndex goods) {
        try {
            //分词器分词
            List<CustomWords> customWordsList = new ArrayList<>();
            List<String> keywordsList = new ArrayList<>();
            if (goods.getAttrList() != null && !goods.getAttrList().isEmpty()) {
                //保存分词
                for (EsGoodsAttribute esGoodsAttribute : goods.getAttrList()) {
                    if (keywordsList.stream().noneMatch(i -> i != null && i.toLowerCase(Locale.ROOT).equals(esGoodsAttribute.getValue().toLowerCase(Locale.ROOT)))) {
                        keywordsList.add(esGoodsAttribute.getValue());
                        customWordsList.add(new CustomWords(esGoodsAttribute.getValue(), 1));
                    }
                }
            }
            if (CollUtil.isNotEmpty(customWordsList)) {
                customWordsClient.deleteBathByName(keywordsList);
                customWordsClient.addCustomWordsList(customWordsList);
            }
        } catch (Exception e) {
            log.info(goods + "分词错误", e);
        }
    }

    /**
     * 更新商品索引的的部分属性（只填写更新的字段，不需要更新的字段不要填写）
     *
     * @param queryFields  查询字段
     * @param updateFields 更新字段
     */
    @Override
    public void updateIndex(Map<String, Object> queryFields, Map<String, Object> updateFields) {
        log.info("==========queryFields============"+ JSON.toJSONString(queryFields)+","+ JSON.toJSONString(updateFields));
        updateIndexFields(queryFields, updateFields, EsGoodsIndex.class);
    }


    @Override
    public void updateGoodsIndexByGoodsList(List<Goods> goodsList) {

        Map<String, Goods> map = new HashMap<>();
        goodsList.forEach(item -> {
            map.put(item.getId(), item);
        });

        goodsList = map.values().stream().toList();

        List<EsGoodsIndex> goodsIndices = new ArrayList<>();
        for (Goods goods : goodsList) {
            //如果商品通过审核&&并且已上架
            GoodsSearchParams searchParams = new GoodsSearchParams();
            searchParams.setGoodsId(goods.getId());
            //searchParams.setGeQuantity(0);
            List<GoodsSku> goodsSkuList = this.goodsSkuService.getGoodsSkuByList(searchParams);
            if (goods.getAuthFlag().equals(GoodsAuthEnum.PASS.name())
                    && goods.getMarketEnable().equals(GoodsMarketEnum.UPPER.name())
                    && Boolean.FALSE.equals(goods.getDeleteFlag())) {
                /**
                 * 以goods为单位，找到sku列表中价格最低的那条记录
                 */
                //会员商品已会员价最低的来
                GoodsSku goodsSku = goodsSkuList.stream().sorted(Comparator.comparing(GoodsSku::getPrice)).collect(Collectors.toList()).get(0);
                boolean b = null != goods.getIsVipGoods() && goods.getIsVipGoods();
                boolean b1 = null != goods.getVipPrice() && goods.getVipPrice() > 0D;
                if(b && b1) {
                    List<GoodsSku> goodsSkuListFilter = goodsSkuList.stream().filter(f -> f.getVipPrice()!=null && f.getVipPrice() > 0).sorted(Comparator.comparing(GoodsSku::getVipPrice)).collect(Collectors.toList());
                    if (CollectionUtil.isNotEmpty(goodsSkuListFilter)) {
                        goodsSku = goodsSkuListFilter.get(0);
                    }
                }
                if (SceneEnums.SUPPLIER.name().equals(goods.getScene())) {
                    EsSupplierGoodsIndex esSupplierGoodsIndex = new EsSupplierGoodsIndex(this.settingUpGoodsIndexDataByGoods(goods, goodsSku));
                    goodsIndices.add(esSupplierGoodsIndex);
                } else {
                    EsGoodsIndex goodsIndex = this.settingUpGoodsIndexDataByGoods(goods, goodsSku);
                    goodsIndices.add(goodsIndex);
                }
                //根据商品ID删除索引
//                this.deleteIndex(
//                        MapUtil.builder(new HashMap<String, Object>())
//                                .put("goodsId", goods.getId()).build(),
//                        goodsIndices.get(0).getClass());


                //this.deleteIndexById(goods.getId());
            }
            //如果商品状态值不支持es搜索，那么将商品信息做下架处理
            else {
//                for (GoodsSku goodsSku : goodsSkuList) {
//                    this.deleteIndexById(goodsSku.getId());
//                }
                //根据商品ID删除，不是根据商品skuId删除，因为商品的最低价可能会发生变化
                if(goods.getScene().equals(SceneEnums.SUPPLIER.name())){
                    this.deleteIndex(
                            MapUtil.builder(new HashMap<String, Object>())
                                    .put("goodsId", goods.getId()).build(),
                            EsSupplierGoodsIndex.class);
                }else{
                    this.deleteIndex(
                            MapUtil.builder(new HashMap<String, Object>())
                                    .put("goodsId", goods.getId()).build(),
                            EsGoodsIndex.class);
                }

                //this.deleteIndexById(goods.getId());
            }
        }
        if(!goodsIndices.isEmpty()){
            goodsIndices.forEach(esGoodsIndex -> {
                this.saveIndex(esGoodsIndex);
            });
            //this.addIndex(goodsIndices);
        }

        //this.restTemplate.save(goodsIndices);
    }

    @Override
    public void updateGoodsIndexByStore(String storeId) {
        //如果商品通过审核&&并且已上架
        GoodsSearchParams searchParams = new GoodsSearchParams();
        searchParams.setStoreId(storeId);
        for (Goods goods : this.goodsService.queryListByParams(searchParams)) {
            this.updateGoodsIndex(goods);
        }

    }

    /**
     * 更新商品索引
     *
     * @param goods 商品消息
     */
    private void updateGoodsIndex(Goods goods) {
        //如果商品通过审核&&并且已上架
        GoodsSearchParams searchParams = new GoodsSearchParams();
        searchParams.setGoodsId(goods.getId());
        List<GoodsSku> goodsSkuList = this.goodsSkuService.getGoodsSkuByList(searchParams);
        log.info("goods：{}", goods);
        log.info("goodsSkuList：{}", goodsSkuList);
        if (goods.getAuthFlag().equals(GoodsAuthEnum.PASS.name())
                && goods.getMarketEnable().equals(GoodsMarketEnum.UPPER.name())
                && Boolean.FALSE.equals(goods.getDeleteFlag())) {
            //this.generatorGoodsIndex(goods, goodsSkuList);
            this.generatorGoodsIndexByGoodsSelf(goods,goodsSkuList);
        }
        //如果商品状态值不支持es搜索，那么将商品信息做下架处理
        else {

//            for (GoodsSku goodsSku : goodsSkuList) {
//                this.deleteIndexById(goodsSku.getId());
//            }
            //this.deleteIndexById(goods.getId());

            //根据商品ID删除，不是根据商品skuId删除，因为商品的最低价可能会发生变化

            this.deleteIndex(
                    MapUtil.builder(new HashMap<String, Object>())
                            .put("goodsId", goods.getId()).build(),
                    goods.getScene().equals(SceneEnums.SUPPLIER.name())? EsSupplierGoodsIndex.class:EsGoodsIndex.class);

            //如果删除的是供应商的商品，则代理商品要下架
            if(goods.getScene().equals(SceneEnums.SUPPLIER.name())){

                List<Goods> proxyGoodsList = SpringContextUtil.getBean(GoodsIntegrationService.class).getProxyGoodsList(goods.getStoreId(),goods.getId());
                if(null!=proxyGoodsList && !proxyGoodsList.isEmpty()){
                    proxyGoodsList.forEach(proxyGoods ->{
                        this.deleteIndex(
                                MapUtil.builder(new HashMap<String, Object>())
                                        .put("goodsId", proxyGoods.getId()).build(),
                                EsGoodsIndex.class);

                    });
                }
            }
        }
    }

    /**
     * 生成商品索引
     *
     * @param goods        商品信息
     * @param goodsSkuList 商品sku信息
     */
    private void generatorGoodsIndex(Goods goods, List<GoodsSku> goodsSkuList) {
        int skuSource = 100;
        List<EsGoodsIndex> esGoodsIndices = new ArrayList<>();
        for (GoodsSku goodsSku : goodsSkuList) {
            EsGoodsIndex goodsIndex = this.settingUpGoodsIndexData(goods, goodsSku);
            skuSource--;
            if (skuSource < 1) {
                skuSource = 1;
            }
            goodsIndex.setSkuSource(skuSource);
            log.info("goodsSku：{}", goodsSku);
            //如果商品库存不为0，并且es中有数据
            if (goodsSku.getQuantity() > 0) {
                log.info("生成商品索引 {}", goodsIndex);
                if (SceneEnums.SUPPLIER.name().equals(goods.getScene())) {
                    EsSupplierGoodsIndex esSupplierGoodsIndex = new EsSupplierGoodsIndex(goodsIndex);
                    esGoodsIndices.add(esSupplierGoodsIndex);
                } else {
                    esGoodsIndices.add(goodsIndex);
                }
            }
        }
        this.deleteIndex(
                MapUtil.builder(new HashMap<String, Object>())
                        .put("goodsId", goods.getId()).build(),
                esGoodsIndices.get(0).getClass());
        this.addIndex(esGoodsIndices);
    }


    /**
     * 生成商品索引
     *
     * @param goods        商品信息
     * @param goodsSkuList 商品sku信息
     */
    private void generatorGoodsIndexByGoodsSelf(Goods goods, List<GoodsSku> goodsSkuList) {
        int skuSource = 100;
        List<EsGoodsIndex> esGoodsIndices = new ArrayList<>();
        //取sku列表价格最低的数据展示
        List<GoodsSku> collect = goodsSkuList.stream().sorted(Comparator.comparing(GoodsSku::getPrice)).collect(Collectors.toList());

        GoodsSku goodsSku = null;
        if (CollectionUtil.isNotEmpty(collect)) {
            goodsSku = collect.get(0);
        }

        if(null!=goodsSku){
            EsGoodsIndex goodsIndex = this.settingUpGoodsIndexDataByGoods(goods, goodsSku);
//            skuSource--;
//            if (skuSource < 1) {
//                skuSource = 1;
//            }
            goodsIndex.setSkuSource(skuSource);
            log.info("goodsSku：{}", goodsSku);
//            if (goodsSku.getQuantity() > 0) {
                log.info("生成商品索引 {}", goodsIndex);
                if (SceneEnums.SUPPLIER.name().equals(goods.getScene())) {
                    EsSupplierGoodsIndex esSupplierGoodsIndex = new EsSupplierGoodsIndex(goodsIndex);
                    esGoodsIndices.add(esSupplierGoodsIndex);
                } else {
                    esGoodsIndices.add(goodsIndex);
                }
//            }
        }
//        for (GoodsSku goodsSku : goodsSkuList) {
//            EsGoodsIndex goodsIndex = this.settingUpGoodsIndexData(goods, goodsSku);
//            skuSource--;
//            if (skuSource < 1) {
//                skuSource = 1;
//            }
//            goodsIndex.setSkuSource(skuSource);
//            log.info("goodsSku：{}", goodsSku);
//            //如果商品库存不为0，并且es中有数据
//            if (goodsSku.getQuantity() > 0) {
//                log.info("生成商品索引 {}", goodsIndex);
//                if (SceneEnums.SUPPLIER.name().equals(goods.getScene())) {
//                    EsSupplierGoodsIndex esSupplierGoodsIndex = new EsSupplierGoodsIndex(goodsIndex);
//                    esGoodsIndices.add(esSupplierGoodsIndex);
//                } else {
//                    esGoodsIndices.add(goodsIndex);
//                }
//            }
//        }

        if(esGoodsIndices.size() == 0) return;
        this.deleteIndex(
                MapUtil.builder(new HashMap<String, Object>())
                        .put("goodsId", goods.getId()).build(),
                esGoodsIndices.get(0).getClass());
        this.addIndex(esGoodsIndices);
    }

    private EsGoodsIndex settingUpGoodsIndexData(Goods goods, GoodsSku goodsSku) {
        EsGoodsIndex goodsIndex = new EsGoodsIndex(goodsSku);
        if (goods.getParams() != null && !goods.getParams().isEmpty()) {
            List<GoodsParamsDTO> goodsParamDTOS = JSONUtil.toList(goods.getParams(), GoodsParamsDTO.class);
            goodsIndex = new EsGoodsIndex(goodsSku, goodsParamDTOS);
        }
        goodsIndex.setAuthFlag(goods.getAuthFlag());
        goodsIndex.setMarketEnable(goods.getMarketEnable());
        goodsIndex.setIntro(goods.getIntro());
        goodsIndex.setMobileIntro(goods.getMobileIntro());
        this.settingUpGoodsIndexOtherParam(goodsIndex);
        return goodsIndex;
    }


    private EsGoodsIndex settingUpGoodsIndexDataByGoods(Goods goods, GoodsSku goodsSku) {
        EsGoodsIndex goodsIndex = new EsGoodsIndex(goods,goodsSku); //传入sku,是回填商品表中没有的字段，如sn,最大F分抵扣等
        if (goods.getParams() != null && !goods.getParams().isEmpty()) {
            List<GoodsParamsDTO> goodsParamDTOS = JSONUtil.toList(goods.getParams(), GoodsParamsDTO.class);
            goodsIndex = new EsGoodsIndex(goods, goodsParamDTOS,goodsSku);
        }
        goodsIndex.setAuthFlag(goods.getAuthFlag());
        goodsIndex.setMarketEnable(goods.getMarketEnable());
        goodsIndex.setIntro(goods.getIntro());
        goodsIndex.setMobileIntro(goods.getMobileIntro());
        this.settingUpGoodsIndexOtherParam(goodsIndex);
        return goodsIndex;
    }

    /**
     * 设置商品索引的其他参数（非商品自带）
     *
     * @param goodsIndex 商品索引信息
     */
    private void settingUpGoodsIndexOtherParam(EsGoodsIndex goodsIndex) {
        if (CharSequenceUtil.isNotEmpty(goodsIndex.getCategoryPath())) {
            List<Category> categories = categoryService.listByIdsOrderByLevel(Arrays.asList(goodsIndex.getCategoryPath().split(",")));
            if (!categories.isEmpty()) {
                goodsIndex.setCategoryNamePath(ArrayUtil.join(categories.stream().map(Category::getName).toArray(), ","));
            }
        }
        Brand brand = brandService.getById(goodsIndex.getBrandId());
        if (brand != null) {
            goodsIndex.setBrandName(brand.getName());
            goodsIndex.setBrandUrl(brand.getLogo());
        }
        if (goodsIndex.getStoreCategoryPath() != null && CharSequenceUtil.isNotEmpty(goodsIndex.getStoreCategoryPath())) {
            List<StoreGoodsLabel> storeGoodsLabels = storeGoodsLabelService.listByStoreIds(Arrays.asList(goodsIndex.getStoreCategoryPath().split(","
            )));
            if (!storeGoodsLabels.isEmpty()) {
                goodsIndex.setStoreCategoryNamePath(ArrayUtil.join(storeGoodsLabels.stream().map(StoreGoodsLabel::getLabelName).toArray(), ","));
            }
        }

        if (goodsIndex.getOriginPromotionMap() == null || goodsIndex.getOriginPromotionMap().isEmpty()) {
            Map<String, Object> goodsCurrentPromotionMap = promotionsClient.getGoodsSkuPromotionMap(goodsIndex.getStoreId(), goodsIndex.getId());
            goodsIndex.setPromotionMapJson(GsonUtils.toJson(goodsCurrentPromotionMap));
        }
    }


    /**
     * 批量商品索引的的属性（ID 必填, 其他字段只填写更新的字段，不需要更新的字段不要填写。）
     *
     * @param goodsIndices 商品索引列表
     */
    @Override
    public void updateBulkIndex(List<EsGoodsIndex> goodsIndices) {
        try {
            BulkRequest request = new BulkRequest();

            for (EsGoodsIndex goodsIndex : goodsIndices) {

                UpdateRequest updateRequest = new UpdateRequest(restTemplate.getIndexCoordinatesFor(goodsIndex.getClass()).getIndexName(),
                        goodsIndex.getId());

                JSONObject jsonObject = JSONUtil.parseObj(goodsIndex);
                jsonObject.set("releaseTime", goodsIndex.getReleaseTime());
                updateRequest.doc(jsonObject);
                request.add(updateRequest);
            }
            client.bulk(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("批量更新商品索引异常", e);
        }
    }

    /**
     * 删除索引
     *
     * @param id 商品索引信息
     */
    @Override
    public void deleteIndexById(String id) {
        deleteIndexById(id, EsGoodsIndex.class);
    }

    /**
     * 删除索引
     *
     * @param ids 商品索引id集合
     */
    @Override
    public void deleteIndexByIds(List<String> ids) {
        deleteIndexByIds(ids, EsGoodsIndex.class);
    }

    @Override
    public void deleteIndex(EsDeleteDTO esDeleteDTO) {
        if (esDeleteDTO.getIds() != null && !esDeleteDTO.getIds().isEmpty()) {
            this.deleteIndexByIds(esDeleteDTO.getIds(), esDeleteDTO.getClazz());
        }
        if (esDeleteDTO.getQueryFields() != null && !esDeleteDTO.getQueryFields().isEmpty()) {
            this.deleteIndex(esDeleteDTO.getQueryFields(), esDeleteDTO.getClazz());
        }
    }

    @Override
    public void initIndex(List<EsGoodsIndex> goodsIndexList, boolean regeneratorIndex, String scene) {
        if (goodsIndexList == null || goodsIndexList.isEmpty()) {
            //初始化标识
            cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), null);
            cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false);
            return;
        }
        //预校验
        recheck(scene, regeneratorIndex);

        Map<String, Long> resultMap = (Map<String, Long>) cache.get(CachePrefix.INIT_INDEX_PROCESS.getPrefix());
        if (!goodsIndexList.isEmpty()) {
            for (EsGoodsIndex goodsIndex : goodsIndexList) {
                try {
                    log.info("生成商品索引：{}", goodsIndex);
                    this.addIndex(goodsIndex);
                    resultMap.put(KEY_SUCCESS, resultMap.get(KEY_SUCCESS) + 1);
                } catch (Exception e) {
                    log.error("商品{}生成索引错误！", goodsIndex);
                    resultMap.put(KEY_FAIL, resultMap.get(KEY_FAIL) + 1);
                }
                resultMap.put(KEY_PROCESSED, resultMap.get(KEY_PROCESSED) + 1);
                cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), resultMap);
            }
        }
        cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), resultMap);
    }


    /**
     * 预校验，不存在则删除
     */
    private void recheck(String scene, boolean regeneratorIndex) {

        if (!regeneratorIndex) {
            return;
        }

        if (SceneEnums.STORE.name().equals(scene)) {
            IndexOperations indexOperations = this.restTemplate.indexOps(EsGoodsIndex.class);
            if (!indexOperations.exists()) {
                //如果索引不存在，则创建索引
                indexOperations.createWithMapping();
            } else {
                indexOperations.delete();
                //如果索引不存在，则创建索引
                indexOperations.createWithMapping();
            }
        } else if (SceneEnums.SUPPLIER.name().equals(scene)) {
            IndexOperations supplierIndexOperations = this.restTemplate.indexOps(EsSupplierGoodsIndex.class);
            if (!supplierIndexOperations.exists()) {
                //如果索引不存在，则创建索引
                supplierIndexOperations.createWithMapping();
            } else {
                this.restTemplate.indexOps(EsSupplierGoodsIndex.class).delete();
                //如果索引不存在，则创建索引
                this.restTemplate.indexOps(EsSupplierGoodsIndex.class).createWithMapping();
            }
        }

    }

    @Override
    public UpdateRequest updateEsGoodsIndexPromotions(String id, BaseStandardPromotions promotion, String key) {
        EsGoodsIndex goodsIndex = findEsGoodsIndexById(id);
        if (goodsIndex != null) {
            //更新索引
            return this.updateGoodsIndexPromotion(goodsIndex, key, promotion);
        } else {
            log.error("更新索引商品促销信息失败！skuId 为 {} 的索引不存在！", id);
            return null;
        }
    }

    /**
     * 更新商品索引的促销信息
     *
     * @param ids       skuId集合
     * @param promotion 促销信息
     * @param key       促销信息的key
     */
    @Override
    public void updateEsGoodsIndexPromotions(List<String> ids, BaseStandardPromotions promotion, String key) {
        BulkRequest bulkRequest = new BulkRequest();
        log.info("更新商品索引的促销信息----------");
        log.info("商品ids: {}", ids);
        log.info("活动: {}", promotion);
        log.info("key: {}", key);
        for (String id : ids) {
            UpdateRequest updateRequest = this.updateEsGoodsIndexPromotions(id, promotion, key);
            if (updateRequest != null) {
                bulkRequest.add(updateRequest);
            }
        }
        this.executeBulkUpdateRequest(bulkRequest);
    }


    @Override
    public void updateEsGoodsIndexByList(List<PromotionGoods> promotionGoodsList, BaseStandardPromotions promotion, String key) {
        BulkRequest bulkRequest = new BulkRequest();
        log.info("修改商品活动索引");
        log.info("促销商品信息: {}", promotionGoodsList);
        log.info("活动关键字: {}", key);
        log.info("活动: {}", promotion);
        if (promotionGoodsList != null) {
            //循环更新 促销商品索引
            for (PromotionGoods promotionGoods : promotionGoodsList) {
                promotion.setStartTime(promotionGoods.getStartTime());
                promotion.setEndTime(promotionGoods.getEndTime());
                UpdateRequest updateRequest = this.updateEsGoodsIndexPromotions(promotionGoods.getSkuId(), promotion, key);
                if (updateRequest != null) {
                    bulkRequest.add(updateRequest);
                }
            }
        }
        this.executeBulkUpdateRequest(bulkRequest);
    }

    /**
     * 更新全部商品索引的促销信息
     *
     * @param promotion 促销信息
     * @param key       促销信息的key
     */
    @Override
    public void updateEsGoodsIndexAllByList(BaseStandardPromotions promotion, String key) {
        ThreadUtil.execAsync(() -> this.executeUpdateAllEsGoodsIndexPromotions(promotion, key));

    }

    /**
     * Executes update for all goods indexes with promotions.
     *
     * @param promotion the promotion information
     * @param key the key for the promotion information
     */
    private void executeUpdateAllEsGoodsIndexPromotions(BaseStandardPromotions promotion, String key) {

        GoodsSearchParams searchParams = new GoodsSearchParams();
        if (PromotionTools.isPromotionsTypeNeedsToChecked(key)) {
            searchParams.setSalesModel(SalesModeEnum.RETAIL.name());
        }
        //如果storeId不为空，则表示是店铺活动
        if (promotion.getStoreId() != null && !promotion.getStoreId().equals(PromotionTools.PLATFORM_ID)) {
            searchParams.setStoreId(promotion.getStoreId());
        }

        //查询出店铺商品
        List<String> skuIds = goodsSkuService.getGoodsSkuIdListByGoodsId(searchParams);
        if (skuIds.isEmpty()) {
            return;
        }
        int i = 0;
        int j = Math.min(skuIds.size(), 1000);
        do {
            List<String> skuIdList = skuIds.subList(i, j);
            this.deleteEsGoodsPromotionByPromotionKey(skuIdList, key);
            this.updateEsGoodsIndexPromotions(skuIdList, promotion, key);
            i = j;
            j += 1000;
        } while (j < skuIds.size());
    }

    @Override
    public void deleteEsGoodsPromotionByPromotionKey(List<String> skuIds, String promotionsKey) {
        BulkRequest bulkRequest = new BulkRequest();
        log.info("删除商品活动索引");
        log.info("商品skuIds: {}", skuIds);
        log.info("活动Key: {}", promotionsKey);
        if (skuIds == null || skuIds.isEmpty()) {
            return;
        }
        for (String skuId : skuIds) {
            EsGoodsIndex goodsIndex = findEsGoodsIndexById(skuId);
            //商品索引不为空
            if (goodsIndex != null) {
                UpdateRequest updateRequest = this.removePromotionByPromotionKey(goodsIndex, promotionsKey);
                if (updateRequest != null) {
                    bulkRequest.add(updateRequest);
                }
            } else {
                log.error("更新索引商品促销信息失败！skuId 为 【{}】的索引不存在！", skuId);
            }
        }
        this.executeBulkUpdateRequest(bulkRequest);
    }

    /**
     * 删除索引中指定的促销活动id的促销活动
     *
     * @param promotionsKey 促销活动Key
     */
    @Override
    public void deleteEsGoodsPromotionByPromotionKey(String promotionsKey) {
        for (int i = 0; ; i++) {
            BulkRequest bulkRequest = new BulkRequest();

            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
            nativeSearchQueryBuilder.withPageable(PageRequest.of(i, 1000));
            try {
                SearchHits<EsGoodsIndex> esGoodsIndices = this.restTemplate.search(nativeSearchQueryBuilder.build(), EsGoodsIndex.class);
                if (esGoodsIndices.isEmpty() || esGoodsIndices.getSearchHits().isEmpty()) {
                    break;
                }
                for (SearchHit<EsGoodsIndex> searchHit : esGoodsIndices.getSearchHits()) {
                    EsGoodsIndex goodsIndex = searchHit.getContent();
                    UpdateRequest updateRequest = this.removePromotionByPromotionKey(goodsIndex, promotionsKey);
                    if (updateRequest != null) {
                        bulkRequest.add(updateRequest);
                    }
                }
                this.executeBulkUpdateRequest(bulkRequest);
            } catch (Exception e) {
                log.error("删除索引中指定的促销活动id的促销活动失败！key: {}", promotionsKey, e);
                return;
            }
        }
    }

    /**
     * 从索引中删除指定促销活动id的促销活动
     *
     * @param goodsIndex    索引
     * @param promotionsKey 促销活动key
     */
    private UpdateRequest removePromotionByPromotionKey(EsGoodsIndex goodsIndex, String promotionsKey) {
        Map<String, Object> promotionMap = goodsIndex.getOriginPromotionMap();
        if (promotionMap != null && !promotionMap.isEmpty()) {
            //如果存在同促销ID的活动删除
            Map<String, Object> filterPromotionMap =
                    promotionMap.entrySet().stream().filter(i -> !i.getKey().equals(promotionsKey)).collect(Collectors.toMap(Map.Entry::getKey,
                            Map.Entry::getValue));
            return this.getGoodsIndexPromotionUpdateRequest(goodsIndex, filterPromotionMap);
        }
        return null;
    }

    /**
     * 清除所有商品索引的无效促销活动
     */
    @Override
    public void cleanInvalidPromotion() {
        for (int i = 0; ; i++) {
            BulkRequest bulkRequest = new BulkRequest();
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
            nativeSearchQueryBuilder.withPageable(PageRequest.of(i, 1000));
            List<EsGoodsIndex> esGoodsIndices = this.findByQuery(nativeSearchQueryBuilder.build(), EsGoodsIndex.class);
            if (esGoodsIndices.isEmpty()) {
                break;
            }
            for (EsGoodsIndex goodsIndex : esGoodsIndices) {
                Map<String, Object> promotionMap = goodsIndex.getOriginPromotionMap();
                if (promotionMap != null && !promotionMap.isEmpty()) {
                    //如果存在同促销ID的活动删除
                    promotionMap.entrySet().removeIf(j -> {
                        JSONObject promotionJson = JSONUtil.parseObj(j.getValue());
                        BaseStandardPromotions promotion = promotionJson.toBean(BaseStandardPromotions.class);
                        return promotion.getEndTime() != null && promotion.getEndTime().getTime() < DateUtil.date().getTime();
                    });
                    bulkRequest.add(this.getGoodsIndexPromotionUpdateRequest(goodsIndex, promotionMap));
                }

            }
            this.executeBulkUpdateRequest(bulkRequest);
        }

    }

    @Override
    public <T> T findIndexById(String id, Class<T> clazz) {
        T index = this.findById(id, clazz);
        if (index == null) {
            log.error("Id为" + id + "的索引不存在！");
            return null;
        }
        return index;
    }

    @Override
    public EsGoodsIndex findEsGoodsIndexById(String id) {
        GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(id);
        if (goodsSku == null) {
            log.error("商品skuId为" + id + "不存在！");
            return null;
        }

        EsGoodsIndex goodsIndex = this.findIndexById(id, goodsSku.getEsIndexClass());
        if (goodsIndex == null) {
            goodsIndex = this.getResetEsGoodsIndex(goodsSku);
        }
        return goodsIndex;
    }

    @Override
    public EsGoodsIndex createTempData(String skuId) {
        GoodsSku goodsSku = goodsSkuService.getById(skuId);
        return createTempData(goodsSku);

    }

    @Override
    public EsGoodsIndex createTempData(GoodsSku goodsSku) {

            if (goodsSku == null || GoodsMarketEnum.DOWN.name().equals(goodsSku.getMarketEnable()) || !GoodsAuthEnum.PASS.name().equals(goodsSku.getAuthFlag()) || Boolean.TRUE.equals(goodsSku.getDeleteFlag())) {
                return null;
            }
            Goods goods = goodsService.getById(goodsSku.getGoodsId());
            if (goods == null) {
                log.error("商品id为" + goodsSku.getGoodsId() + "不存在！");
                return null;
            }
            GoodsSkuDTO goodsSkuDTO = new GoodsSkuDTO(goodsSku, JSONUtil.toList(goods.getParams(), GoodsParamsDTO.class));

            List<PromotionGoods> skuValidPromotions = promotionsClient.findSkuValidPromotions(Collections.singletonList(goodsSkuDTO));

            List<String> brandIds = new ArrayList<>();

            List<String> categoryPaths = new ArrayList<>();

            List<String> storeCategoryPaths = new ArrayList<>();

            if (CharSequenceUtil.isNotEmpty(goodsSkuDTO.getBrandId())) {
                brandIds.add(goodsSkuDTO.getBrandId());
            }
            if (CharSequenceUtil.isNotEmpty(goodsSkuDTO.getStoreCategoryPath())) {
                storeCategoryPaths.addAll(Arrays.asList(goodsSkuDTO.getStoreCategoryPath().split(",")));
            }
            if (CharSequenceUtil.isNotEmpty((goodsSkuDTO.getCategoryPath()))) {
                categoryPaths.addAll(Arrays.asList(goodsSkuDTO.getCategoryPath().split(",")));
            }

            List<Map<String, Object>> brandList = new ArrayList<>();
            if (CollUtil.isNotEmpty(brandIds)) {
                brandList = this.brandService.getBrandsMapsByCategory(CollUtil.distinct(brandIds), "id,name,logo");
            }
            List<Map<String, Object>> categoryList = new ArrayList<>();
            if (CollUtil.isNotEmpty(categoryPaths)) {
                categoryList = this.categoryService.listMapsByIdsOrderByLevel(CollUtil.distinct(categoryPaths), "id,name");
            }

            List<Map<String, Object>> storeCategoryList = new ArrayList<>();
            if (CollUtil.isNotEmpty(storeCategoryPaths)) {
                storeCategoryList = this.storeGoodsLabelService.listMapsByStoreIds(CollUtil.distinct(storeCategoryPaths), "id,label_name");
            }

            EsGoodsIndex index = wrapperEsGoodsIndex(goodsSkuDTO, brandList, categoryList, storeCategoryList);
            if (SceneEnums.SUPPLIER.name().equals(goodsSku.getScene())) {
                index = new EsSupplierGoodsIndex(index);
            }
            //设置促销信息
            List<PromotionGoods> promotionGoods = skuValidPromotions.stream()
                    .filter(j ->
                            (CharSequenceUtil.isNotEmpty(j.getSkuId()) && j.getSkuId().equals(goodsSku.getId())) ||
                                    (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals("0")) ||
                                    (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals(goodsSku.getStoreId())) ||
                                    (j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getScopeId().contains(goodsSku.getCategoryPath())))
                    .toList();
            if (CollUtil.isNotEmpty(promotionGoods)) {
                index.setPromotionMapJson(GsonUtils.toJson(promotionsClient.wrapperPromotionMapList(promotionGoods)));
            }

            return index;


    }


    /**
     * 根据id获取商品索引信息的促销信息
     *
     * @param id skuId
     * @return 促销信息map
     */
    @Override
    public Map<String, Object> getPromotionMap(String id) {
        EsGoodsIndex goodsIndex = this.findEsGoodsIndexById(id);

        //如果商品索引不为空，返回促销信息，否则返回空
        if (goodsIndex != null) {
            Map<String, Object> promotionMap = goodsIndex.getOriginPromotionMap();
            if (promotionMap == null || promotionMap.isEmpty()) {
                return new HashMap<>(16);
            }
            return promotionMap;
        }
        return new HashMap<>();
    }

    /**
     * 根据id获取商品索引信息的指定促销活动的id
     *
     * @param id                skuId
     * @param promotionTypeEnum 促销活动类型
     * @return 当前商品参与的促销活动id集合
     */
    @Override
    public List<String> getPromotionIdByPromotionType(String id, PromotionTypeEnum promotionTypeEnum) {
        Map<String, Object> promotionMap = this.getPromotionMap(id);
        //如果没有促销信息，则返回新的
        if (promotionMap == null || promotionMap.isEmpty()) {
            return new ArrayList<>();
        }
        //对促销进行过滤
        List<String> keyCollect = promotionMap.keySet().stream().filter(i -> i.contains(promotionTypeEnum.name())).toList();
        List<String> promotionIds = new ArrayList<>();
        //写入促销id
        for (String key : keyCollect) {
            BaseStandardPromotions promotion = (BaseStandardPromotions) promotionMap.get(key);
            promotionIds.add(promotion.getId());
        }
        return promotionIds;
    }

    /**
     * 获取重置的商品索引
     *
     * @param goodsSku 商品sku信息
     * @return 商品索引
     */
    @Override
    public EsGoodsIndex getResetEsGoodsIndex(GoodsSku goodsSku) {
        if (GoodsMarketEnum.DOWN.name().equals(goodsSku.getMarketEnable()) || !GoodsAuthEnum.PASS.name().equals(goodsSku.getAuthFlag()) || Boolean.TRUE.equals(goodsSku.getDeleteFlag())) {
            return null;
        }
        Goods goods = goodsService.getById(goodsSku.getGoodsId());
        if (goods == null) {
            log.error("商品id为" + goodsSku.getGoodsId() + "不存在！");
            return null;
        }
        GoodsSkuDTO goodsSkuDTO = new GoodsSkuDTO(goodsSku, JSONUtil.toList(goods.getParams(), GoodsParamsDTO.class));

        List<PromotionGoods> skuValidPromotions = promotionsClient.findSkuValidPromotions(Collections.singletonList(goodsSkuDTO));

        List<String> brandIds = new ArrayList<>();

        List<String> categoryPaths = new ArrayList<>();

        List<String> storeCategoryPaths = new ArrayList<>();

        if (CharSequenceUtil.isNotEmpty(goodsSkuDTO.getBrandId())) {
            brandIds.add(goodsSkuDTO.getBrandId());
        }
        if (CharSequenceUtil.isNotEmpty(goodsSkuDTO.getStoreCategoryPath())) {
            storeCategoryPaths.addAll(Arrays.asList(goodsSkuDTO.getStoreCategoryPath().split(",")));
        }
        if (CharSequenceUtil.isNotEmpty((goodsSkuDTO.getCategoryPath()))) {
            categoryPaths.addAll(Arrays.asList(goodsSkuDTO.getCategoryPath().split(",")));
        }

        List<Map<String, Object>> brandList = new ArrayList<>();
        if (CollUtil.isNotEmpty(brandIds)) {
            brandList = this.brandService.getBrandsMapsByCategory(CollUtil.distinct(brandIds), "id,name,logo");
        }
        List<Map<String, Object>> categoryList = new ArrayList<>();
        if (CollUtil.isNotEmpty(categoryPaths)) {
            categoryList = this.categoryService.listMapsByIdsOrderByLevel(CollUtil.distinct(categoryPaths), "id,name");
        }
        List<Map<String, Object>> storeCategoryList = new ArrayList<>();
        if (CollUtil.isNotEmpty(storeCategoryPaths)) {
            storeCategoryList = this.storeGoodsLabelService.listMapsByStoreIds(CollUtil.distinct(storeCategoryPaths), "id,label_name");
        }

        EsGoodsIndex index = wrapperEsGoodsIndex(goodsSkuDTO, brandList, categoryList, storeCategoryList);
        if (SceneEnums.SUPPLIER.name().equals(goodsSku.getScene())) {
            index = new EsSupplierGoodsIndex(index);
        }
        //设置促销信息
        List<PromotionGoods> promotionGoods = skuValidPromotions.stream()
                .filter(j ->
                        (CharSequenceUtil.isNotEmpty(j.getSkuId()) && j.getSkuId().equals(goodsSku.getId())) ||
                                (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals("0")) ||
                                (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals(goodsSku.getStoreId())) ||
                                (j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getScopeId().contains(goodsSku.getCategoryPath())))
                .toList();
        if (CollUtil.isNotEmpty(promotionGoods)) {
            index.setPromotionMapJson(GsonUtils.toJson(promotionsClient.wrapperPromotionMapList(promotionGoods)));
        }


        return this.saveIndex(index);
    }


    /**
     * 修改商品活动索引
     *
     * @param goodsIndex 商品索引
     * @param key        关键字
     * @param promotion  活动
     */
    private UpdateRequest updateGoodsIndexPromotion(EsGoodsIndex goodsIndex, String key, BaseStandardPromotions promotion) {
        Map<String, Object> promotionMap;
        //数据非空处理，如果空给一个新的信息
        if (goodsIndex.getOriginPromotionMap() == null || goodsIndex.getOriginPromotionMap().isEmpty()) {
            promotionMap = new HashMap<>(1);
        } else {
            promotionMap = goodsIndex.getOriginPromotionMap();
        }
        //如果活动已结束
        if (promotion.getPromotionStatus().equals(PromotionsStatusEnum.END.name()) || promotion.getPromotionStatus().equals(PromotionsStatusEnum.CLOSE.name())) {//如果存在活动
            //删除活动
            promotionMap.remove(key);
        } else {
            promotionMap.put(key, promotion);
        }
        return this.getGoodsIndexPromotionUpdateRequest(goodsIndex, promotionMap);
    }

    /**
     * 以更新部分字段的方式更新索引促销信息
     *
     * @param goodsIndex   索引
     * @param promotionMap 促销信息
     */
    private UpdateRequest getGoodsIndexPromotionUpdateRequest(EsGoodsIndex goodsIndex, Map<String, Object> promotionMap) {
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index(this.restTemplate.indexOps(goodsIndex.getClass()).getIndexCoordinates().getIndexName());
        updateRequest.id(goodsIndex.getId());
        updateRequest.retryOnConflict(3);
        Map<String, Object> params = new HashMap<>();
        params.put("promotionMap", GsonUtils.toJson(promotionMap));
        Script script = new Script(ScriptType.INLINE, "painless", "ctx._source.promotionMapJson=params.promotionMap;", params);
        updateRequest.script(script);
        return updateRequest;
    }

    /**
     * 执行批量更新商品索引
     *
     * @param bulkRequest 批量请求
     */
    private void executeBulkUpdateRequest(BulkRequest bulkRequest) {
        if (bulkRequest.requests().isEmpty()) {
            return;
        }
        try {
            BulkResponse responses = this.client.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (responses.hasFailures()) {
                log.info("批量更新商品索引的促销信息中出现部分异常：{}", responses.buildFailureMessage());
            } else {
                log.info("批量更新商品索引的促销信息结果：{}", responses.status());
            }
        } catch (IOException e) {
            log.error("批量更新商品索引的促销信息出现异常！", e);
        }
    }

    /**
     * 移除需要移除的促销活动
     *
     * @param currentKey     当前操作的促销活动key
     * @param promotionMap   促销活动
     * @param needRemoveKeys 需移除的促销活动
     */
    private void removePromotionKey(String currentKey, Map<String, Object> promotionMap, String... needRemoveKeys) {
        //判定是否需要移除
        if (CharSequenceUtil.containsAny(currentKey, needRemoveKeys)) {

            List<String> removeKeys = new ArrayList<>();
            //促销循环
            for (String entry : promotionMap.keySet()) {
                //需要移除则进行移除处理
                for (String needRemoveKey : needRemoveKeys) {
                    if (entry.contains(needRemoveKey) && currentKey.contains(needRemoveKey)) {
                        removeKeys.add(entry);
                        break;
                    }
                }
            }
            //移除促销信息
            removeKeys.forEach(promotionMap.keySet()::remove);
        }
    }

    private void generatorEsGoodsIndex(String scene) {
        try {
            GoodsSearchParams searchParams = new GoodsSearchParams();
            searchParams.setAuthFlag(GoodsAuthEnum.PASS.name());
            searchParams.setMarketEnable(GoodsMarketEnum.UPPER.name());
            searchParams.setScene(scene);


            Map<String, Long> resultMap = (Map<String, Long>) cache.get(CachePrefix.INIT_INDEX_PROCESS.getPrefix());

            if (CollUtil.isEmpty(resultMap)) {
                resultMap = new HashMap<>();
                resultMap.put(KEY_SUCCESS, 0L);
                resultMap.put(KEY_FAIL, 0L);
                resultMap.put(KEY_PROCESSED, 0L);
                resultMap.put("total", this.goodsSkuService.count(searchParams.queryWrapper()));
                cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), resultMap);
            }

            for (int i = 1; ; i++) {
                List<EsGoodsIndex> esGoodsIndices = new ArrayList<>();
                searchParams.setPageSize(2000);
                searchParams.setPageNumber(i);
                IPage<GoodsSkuDTO> skuIPage = goodsSkuService.getGoodsSkuDTOByPage(PageUtil.initPage(searchParams),
                        searchParams.queryWrapperGoodsSkuByCustomSql());
                if (skuIPage == null || CollUtil.isEmpty(skuIPage.getRecords())) {
                    break;
                }

                List<PromotionGoods> skuValidPromotions = promotionsClient.findSkuValidPromotions(skuIPage.getRecords());

                List<String> brandIds = new ArrayList<>();

                List<String> categoryPaths = new ArrayList<>();

                List<String> storeCategoryPaths = new ArrayList<>();

                for (GoodsSkuDTO goodsSkuDTO : skuIPage.getRecords()) {
                    if (CharSequenceUtil.isNotEmpty(goodsSkuDTO.getBrandId())) {
                        brandIds.add(goodsSkuDTO.getBrandId());
                    }
                    if (CharSequenceUtil.isNotEmpty(goodsSkuDTO.getStoreCategoryPath())) {
                        storeCategoryPaths.addAll(Arrays.asList(goodsSkuDTO.getStoreCategoryPath().split(",")));
                    }
                    if (CharSequenceUtil.isNotEmpty((goodsSkuDTO.getCategoryPath()))) {
                        categoryPaths.addAll(Arrays.asList(goodsSkuDTO.getCategoryPath().split(",")));
                    }
                }

                List<Map<String, Object>> brandList = new ArrayList<>();
                if (CollUtil.isNotEmpty(brandIds)) {
                    brandList = this.brandService.getBrandsMapsByCategory(CollUtil.distinct(brandIds), "id,name,logo");
                }
                List<Map<String, Object>> categoryList = new ArrayList<>();
                if (CollUtil.isNotEmpty(categoryPaths)) {
                    categoryList = this.categoryService.listMapsByIdsOrderByLevel(CollUtil.distinct(categoryPaths), "id,name");
                }
                List<Map<String, Object>> storeCategoryList = new ArrayList<>();
                if (CollUtil.isNotEmpty(storeCategoryPaths)) {
                    storeCategoryList = this.storeGoodsLabelService.listMapsByStoreIds(CollUtil.distinct(storeCategoryPaths), "id,label_name");
                }

                for (GoodsSkuDTO goodsSku : skuIPage.getRecords()) {
                    int skuSource = 100;
                    EsGoodsIndex esGoodsIndex = wrapperEsGoodsIndex(goodsSku, brandList, categoryList, storeCategoryList);
                    if (SceneEnums.SUPPLIER.name().equals(goodsSku.getScene())) {
                        esGoodsIndex = new EsSupplierGoodsIndex(esGoodsIndex);
                    }
                    String goodsId = esGoodsIndex.getGoodsId();
                    long count = esGoodsIndices.stream().filter(j -> j.getGoodsId().equals(goodsId)).count();
                    if (count >= 1) {
                        skuSource -= (int) count;
                    }
                    if (skuSource < 1) {
                        skuSource = 1;
                    }
                    esGoodsIndex.setSkuSource(skuSource);
                    //设置促销信息
                    List<PromotionGoods> promotionGoods = skuValidPromotions.stream()
                            .filter(j ->
                                    (CharSequenceUtil.isNotEmpty(j.getSkuId()) && j.getSkuId().equals(goodsSku.getId())) ||
                                            (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals("0")) ||
                                            (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals(goodsSku.getStoreId())) ||
                                            (j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getScopeId().contains(goodsSku.getCategoryPath())))
                            .toList();
                    if (CollUtil.isNotEmpty(promotionGoods)) {
                        esGoodsIndex.setPromotionMapJson(GsonUtils.toJson(promotionsClient.wrapperPromotionMapList(promotionGoods)));
                    }
                    esGoodsIndices.add(esGoodsIndex);
                    //库存锁是在redis做的，所以生成索引，同时更新一下redis中的库存数量
                    cache.put(GoodsSkuClient.getStockCacheKey(goodsSku), goodsSku.getQuantity());
                }

                //批量插入索引，如果为第一次则删除原索引并创建新索引
                this.initIndex(esGoodsIndices, i == 1, scene);
            }

            cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false);

            //初始化商品索引
        } catch (Exception e) {
            log.error("商品索引生成异常：", e);
            //如果出现异常，则将进行中的任务标识取消掉，打印日志
            cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), null);
            cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false);
        }
    }


    /**
     * 以goods为单位建议索引
     * @param scene
     */
    private void generatorEsGoodsIndexByGoods(String scene) {
        try {
            GoodsSearchParams searchParams = new GoodsSearchParams();
            searchParams.setAuthFlag(GoodsAuthEnum.PASS.name());
            searchParams.setMarketEnable(GoodsMarketEnum.UPPER.name());
            searchParams.setScene(scene);


            Map<String, Long> resultMap = (Map<String, Long>) cache.get(CachePrefix.INIT_INDEX_PROCESS.getPrefix());

            if (CollUtil.isEmpty(resultMap)) {
                resultMap = new HashMap<>();
                resultMap.put(KEY_SUCCESS, 0L);
                resultMap.put(KEY_FAIL, 0L);
                resultMap.put(KEY_PROCESSED, 0L);
                //resultMap.put("total", this.goodsSkuService.count(searchParams.queryWrapper()));
                resultMap.put("total", this.goodsService.count(searchParams.queryWrapper()));
                cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), resultMap);
            }

            for (int i = 1; ; i++) {
                List<EsGoodsIndex> esGoodsIndices = new ArrayList<>();
                searchParams.setPageSize(2000);
                searchParams.setPageNumber(i);
                IPage<Goods> goodsPage = goodsService.queryGoodsByParams(searchParams);
                if (goodsPage == null || CollUtil.isEmpty(goodsPage.getRecords())) {
                    break;
                }
                log.info("goodsPage == {}", goodsPage.getRecords().size());
                //log.info("==============goodsPage=="+ JSON.toJSONString(goodsPage));
                List<String> brandIds = new ArrayList<>();

                List<String> categoryPaths = new ArrayList<>();

                List<String> storeCategoryPaths = new ArrayList<>();

                for (Goods goods : goodsPage.getRecords()) {
                    if (CharSequenceUtil.isNotEmpty(goods.getBrandId())) {
                        brandIds.add(goods.getBrandId());
                    }
                    if (CharSequenceUtil.isNotEmpty(goods.getStoreCategoryPath())) {
                        storeCategoryPaths.addAll(Arrays.asList(goods.getStoreCategoryPath().split(",")));
                    }
                    if (CharSequenceUtil.isNotEmpty((goods.getCategoryPath()))) {
                        categoryPaths.addAll(Arrays.asList(goods.getCategoryPath().split(",")));
                    }
                }

                List<Map<String, Object>> brandList = new ArrayList<>();
                if (CollUtil.isNotEmpty(brandIds)) {
                    brandList = this.brandService.getBrandsMapsByCategory(CollUtil.distinct(brandIds), "id,name,logo");
                }
                List<Map<String, Object>> categoryList = new ArrayList<>();
                if (CollUtil.isNotEmpty(categoryPaths)) {
                    categoryList = this.categoryService.listMapsByIdsOrderByLevel(CollUtil.distinct(categoryPaths), "id,name");
                }
                List<Map<String, Object>> storeCategoryList = new ArrayList<>();
                if (CollUtil.isNotEmpty(storeCategoryPaths)) {
                    storeCategoryList = this.storeGoodsLabelService.listMapsByStoreIds(CollUtil.distinct(storeCategoryPaths), "id,label_name");
                }

                for (Goods goods : goodsPage.getRecords()) {
                    int skuSource = 100;
                    GoodsSearchParams searchParams1 = new GoodsSearchParams();
                    searchParams1.setGoodsId(goods.getId());
                    searchParams1.setAuthFlag(GoodsAuthEnum.PASS.name());
                    searchParams1.setMarketEnable(GoodsMarketEnum.UPPER.name());
                    searchParams1.setScene(scene);
                    searchParams1.setPageSize(1000);
                    //log.info("==============goods=="+ JSON.toJSONString(goods));
                    IPage<GoodsSkuDTO> skuIPage = goodsSkuService.getGoodsSkuDTOByPage(PageUtil.initPage(searchParams1),
                            searchParams1.queryWrapperGoodsSkuByCustomSql());
                    //log.info("==============skuIPage=="+ JSON.toJSONString(skuIPage));

                    List<GoodsSkuDTO> skuList = skuIPage.getRecords();
                    if(null == skuList || skuList.size()<1){
                        continue;
                    }

                    GoodsSkuDTO goodsSku = skuList.stream().sorted(Comparator.comparing(GoodsSku::getPrice)).collect(Collectors.toList()).get(0);
                    EsGoodsIndex esGoodsIndex = wrapperEsGoodsIndexByGoods(goods, goodsSku, brandList, categoryList, storeCategoryList);
                    if (SceneEnums.SUPPLIER.name().equals(goods.getScene())) {
                        esGoodsIndex = new EsSupplierGoodsIndex(esGoodsIndex);
                    }

                    //设置当前sku的促销数据
                    List<GoodsSkuDTO> ll = new ArrayList<>();
                    ll.add(goodsSku);
                    List<PromotionGoods> skuValidPromotions = promotionsClient.findSkuValidPromotions(ll);
                    List<PromotionGoods> promotionGoods = skuValidPromotions.stream()
                            .filter(j ->
                                    (CharSequenceUtil.isNotEmpty(j.getSkuId()) && j.getSkuId().equals(goodsSku.getId())) ||
                                            (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals("0")) ||
                                            (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals(goodsSku.getStoreId())) ||
                                            (j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getScopeId().contains(goodsSku.getCategoryPath())))
                            .toList();
                    if (CollUtil.isNotEmpty(promotionGoods)) {
                        esGoodsIndex.setPromotionMapJson(GsonUtils.toJson(promotionsClient.wrapperPromotionMapList(promotionGoods)));
                    }


//                    if (null!=skuIPage  && CollUtil.isNotEmpty(skuIPage.getRecords())) {
//                        List<PromotionGoods> skuValidPromotions = promotionsClient.findSkuValidPromotions(skuList);
//                        //设置促销信息
//                        List<PromotionGoods> promotionGoods = skuValidPromotions.stream()
//                                .filter(j ->
//                                        (CharSequenceUtil.isNotEmpty(j.getSkuId()) && j.getSkuId().equals(goods.getId())) ||
//                                                (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals("0")) ||
//                                                (j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals(goods.getStoreId())) ||
//                                                (j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getScopeId().contains(goods.getCategoryPath())))
//                                .toList();
//                        if (CollUtil.isNotEmpty(promotionGoods)) {
//                            esGoodsIndex.setPromotionMapJson(GsonUtils.toJson(promotionsClient.wrapperPromotionMapList(promotionGoods)));
//                        }
//                        esGoodsIndices.add(esGoodsIndex);
//                    }

                    String goodsId = esGoodsIndex.getGoodsId();
                    long count = esGoodsIndices.stream().filter(j -> j.getGoodsId().equals(goodsId)).count();
                    if (count >= 1) {
                        skuSource -= (int) count;
                    }
                    if (skuSource < 1) {
                        skuSource = 1;
                    }
                    esGoodsIndex.setSkuSource(skuSource);

                    //库存锁是在redis做的，所以生成索引，同时更新一下redis中的库存数量
                    skuList.forEach(sku -> {
                        cache.put(GoodsSkuClient.getStockCacheKey(sku), sku.getQuantity());
                    });

                    esGoodsIndices.add(esGoodsIndex);

                }

                log.info("==============esGoodsIndices=="+  esGoodsIndices.size());

                //批量插入索引，如果为第一次则删除原索引并创建新索引
                this.initIndex(esGoodsIndices, i == 1, scene);

            }

            cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false);

            //初始化商品索引
        } catch (Exception e) {
            log.error("商品索引生成异常：", e);
            //如果出现异常，则将进行中的任务标识取消掉，打印日志
            cache.put(CachePrefix.INIT_INDEX_PROCESS.getPrefix(), null);
            cache.put(CachePrefix.INIT_INDEX_FLAG.getPrefix(), false);
        }
    }


    private EsGoodsIndex wrapperEsGoodsIndex(GoodsSkuDTO goodsSku, List<Map<String, Object>> brandList,
                                             List<Map<String, Object>> categoryList,
                                             List<Map<String, Object>> storeCategoryList) {
        EsGoodsIndex index = new EsGoodsIndex(goodsSku);

        //商品参数索引
        if (CharSequenceUtil.isNotEmpty(goodsSku.getParams())) {
            List<GoodsParamsDTO> goodsParamDTOS = JSONUtil.toList(goodsSku.getParams(), GoodsParamsDTO.class);
            index = new EsGoodsIndex(goodsSku, goodsParamDTOS);
        }
        //商品分类索引
        if (CollUtil.isNotEmpty(categoryList) && CharSequenceUtil.isNotEmpty(goodsSku.getCategoryPath())) {
            StringBuilder categoryNamePath = new StringBuilder();
            categoryList.stream().filter(o -> goodsSku.getCategoryPath().contains(o.get("id").toString())).forEach(p -> categoryNamePath.append(p.get("name")).append(","));
            if (CharSequenceUtil.isNotEmpty(categoryNamePath)) {
                categoryNamePath.deleteCharAt(categoryNamePath.length() - 1);
                index.setCategoryNamePath(categoryNamePath.toString());
            }
        }
        //商品品牌索引
        if (CollUtil.isNotEmpty(brandList) && CharSequenceUtil.isNotEmpty(goodsSku.getBrandId())) {
            Optional<Map<String, Object>> brandInfo =
                    brandList.stream().filter(p -> p.get("id").toString().equals(goodsSku.getBrandId())).findFirst();
            if (brandInfo.isPresent()) {
                index.setBrandName(brandInfo.get().get("name").toString());
                index.setBrandUrl(brandInfo.get().get("logo").toString());
            }
        }
        //店铺分类索引
        if (CollUtil.isNotEmpty(storeCategoryList) && CharSequenceUtil.isNotEmpty(goodsSku.getStoreCategoryPath())) {
            StringBuilder storeCategoryNamePath = new StringBuilder();
            storeCategoryList.stream().filter(o -> goodsSku.getStoreCategoryPath().contains(o.get("id").toString())).forEach(p -> storeCategoryNamePath.append(p.get("label_name").toString()).append(","));
            if (CharSequenceUtil.isNotEmpty(storeCategoryNamePath)) {
                storeCategoryNamePath.deleteCharAt(storeCategoryNamePath.length() - 1);
                index.setStoreCategoryNamePath(storeCategoryNamePath.toString());
            }
        }
        return index;
    }



    private EsGoodsIndex wrapperEsGoodsIndexByGoods(Goods goods, GoodsSku goodsSku,List<Map<String, Object>> brandList,
                                                    List<Map<String, Object>> categoryList,
                                                    List<Map<String, Object>> storeCategoryList) {
        EsGoodsIndex index = new EsGoodsIndex(goods,goodsSku);

        //商品参数索引
        if (CharSequenceUtil.isNotEmpty(goods.getParams())) {
            List<GoodsParamsDTO> goodsParamDTOS = JSONUtil.toList(goods.getParams(), GoodsParamsDTO.class);
            index = new EsGoodsIndex(goods, goodsParamDTOS,goodsSku);
        }
        //商品分类索引
        if (CollUtil.isNotEmpty(categoryList) && CharSequenceUtil.isNotEmpty(goodsSku.getCategoryPath())) {
            StringBuilder categoryNamePath = new StringBuilder();
            categoryList.stream().filter(o -> goods.getCategoryPath().contains(o.get("id").toString())).forEach(p -> categoryNamePath.append(p.get("name")).append(","));
            if (CharSequenceUtil.isNotEmpty(categoryNamePath)) {
                categoryNamePath.deleteCharAt(categoryNamePath.length() - 1);
                index.setCategoryNamePath(categoryNamePath.toString());
            }
        }
        //商品品牌索引
        if (CollUtil.isNotEmpty(brandList) && CharSequenceUtil.isNotEmpty(goodsSku.getBrandId())) {
            Optional<Map<String, Object>> brandInfo =
                    brandList.stream().filter(p -> p.get("id").toString().equals(goods.getBrandId())).findFirst();
            if (brandInfo.isPresent()) {
                index.setBrandName(brandInfo.get().get("name").toString());
                index.setBrandUrl(brandInfo.get().get("logo").toString());
            }
        }
        //店铺分类索引
        if (CollUtil.isNotEmpty(storeCategoryList) && CharSequenceUtil.isNotEmpty(goodsSku.getStoreCategoryPath())) {
            StringBuilder storeCategoryNamePath = new StringBuilder();
            storeCategoryList.stream().filter(o -> goods.getStoreCategoryPath().contains(o.get("id").toString())).forEach(p -> storeCategoryNamePath.append(p.get("label_name").toString()).append(","));
            if (CharSequenceUtil.isNotEmpty(storeCategoryNamePath)) {
                storeCategoryNamePath.deleteCharAt(storeCategoryNamePath.length() - 1);
                index.setStoreCategoryNamePath(storeCategoryNamePath.toString());
            }
        }
        return index;
    }

    @Override
    public <T> Iterable<T> saveIndex(Iterable<T> t) {

        t.forEach(e -> {
            deleteIndex(e);
        });


        return super.saveIndex(t);
    }

    @Override
    public <T> T saveIndex(T t) {
        deleteIndex(t);

        return super.saveIndex(t);
    }

    private <T> T deleteIndex(T t){
        if(t instanceof EsGoodsIndex) {
            EsGoodsIndex goods = (EsGoodsIndex) t;
            List<EsGoodsIndex> goodsIndices = new ArrayList<>();
            goodsIndices.add(goods);
            this.deleteIndex(
                    MapUtil.builder(new HashMap<String, Object>())
                            .put("goodsId", goods.getId()).build(),
                    goodsIndices.get(0).getClass());
        }

        if(t instanceof EsSupplierGoodsIndex) {
            EsSupplierGoodsIndex goods = (EsSupplierGoodsIndex) t;
            List<EsSupplierGoodsIndex> goodsIndices = new ArrayList<>();
            goodsIndices.add(goods);
            this.deleteIndex(
                    MapUtil.builder(new HashMap<String, Object>())
                            .put("goodsId", goods.getId()).build(),
                    goodsIndices.get(0).getClass());
        }
        return null;
    }
}
